package cn.jjxx.core.utils;

import org.apache.commons.lang3.StringUtils;

import java.math.BigDecimal;

/**
 * @Title: BigDecimal工具类
 * @Description: BigDecimal工具类，提供加减乘除方法
 * @author mali
 * @date 2018-05-29
 * @version V1.0
 */
public class BigDecimalUtil {
    private BigDecimalUtil() {}

    /**
     * 提供精确的小数位四舍五入处理。
     * @param v 需要四舍五入的数字
     * @param scale 小数点后保留几位
     * @return 四舍五入后的结果
     */
    public static double round(double v, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("精确度不能小于0");
        }
        BigDecimal b = new BigDecimal(Double.toString(v));
        BigDecimal one = new BigDecimal("1");
        return b.divide(one, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * 提供精确的小数位四舍五入处理。
     * @param v 需要四舍五入的数字
     * @param scale 小数点后保留几位
     * @return 四舍五入后的结果
     */
    public static BigDecimal roundD(double v, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("精确度不能小于0");
        }
        BigDecimal b = new BigDecimal(Double.toString(v));
        BigDecimal one = new BigDecimal("1");
        return b.divide(one, scale, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 加法，不进行四舍五入
     * @param v1 被加数
     * @param v2 加数
     * @return double 两个参数的和
     */
    public static double add(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.add(b2).doubleValue();
    }

    /**
     * 加法，四舍五入
     * @param v1 被加数
     * @param v2 加数
     * @param scale 保留小数位数
     * @return double 两个参数的和
     */
    public static double add(String v1, String v2, int scale) {
        //如果精确范围小于0，默认为2位小数
        if(scale < 0){
            scale = 2;
        }
        BigDecimal b1 = new BigDecimal(org.apache.commons.lang3.StringUtils.isBlank(v1) ? "0" : v1);
        BigDecimal b2 = new BigDecimal(org.apache.commons.lang3.StringUtils.isBlank(v2) ? "0" : v2);
        return round(b1.add(b2).doubleValue(), scale);
    }

    /**
     * 加法，不进行四舍五入
     * @param v1 被加数
     * @param v2 加数
     * @return double 两个参数的和
     */
    public static double add(BigDecimal v1, BigDecimal v2) {
        return v1.add(v2).doubleValue();
    }

    /**
     * 加法，不进行四舍五入
     * @param v1 被加数
     * @param v2 加数
     * @return BigDecimal 两个参数的和
     */
    public static BigDecimal addD(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.add(b2);
    }

    /**
     * 加法，四舍五入，默认2位小数
     * @param v1 被加数
     * @param v2 加数
     * @param scale 保留小数位数
     * @return BigDecimal 两个参数的和
     */
    public static BigDecimal addD(String v1, String v2, int scale) {
        //如果精确范围小于0，默认为2位小数
        if(scale < 0){
            scale = 2;
        }
        BigDecimal b1 = new BigDecimal(org.apache.commons.lang3.StringUtils.isBlank(v1) ? "0" : v1);
        BigDecimal b2 = new BigDecimal(org.apache.commons.lang3.StringUtils.isBlank(v2) ? "0" : v2);
        return roundD(b1.add(b2).doubleValue(), scale);
    }

    /**
     * 加法，四舍五入，默认2位小数
     * @param v1 被加数
     * @param v2 加数
     * @param scale 保留小数位数
     * @return BigDecimal 两个参数的和
     */
    public static BigDecimal addD(BigDecimal v1, BigDecimal v2, int scale) {
        //如果精确范围小于0，默认为2位小数
        if(scale < 0){
            scale = 2;
        }
        return roundD(v1.add(v2).doubleValue(), scale);
    }

    /**
     * 加法
     * @param scale 保留小数位数
     * @param v 需要的加数
     * @return double 所有参数的和
     */
    public static double add(int scale, String... v) {
        //如果精确范围小于0，默认为2位小数
        if(scale < 0){
            scale = 2;
        }
        BigDecimal result = new BigDecimal("0");
        if (v != null && v.length > 0) {
            for (int i = 0; i <  v.length; i++) {
                result = result.add(new BigDecimal(org.apache.commons.lang3.StringUtils.isBlank(v[i]) ? "0" : v[i]));
            }
        }
        return round(result.doubleValue(), scale);
    }

    /**
     * 加法
     * @param scale 保留小数位数
     * @param v 需要的加数
     * @return BigDecimal 所有参数的和
     */
    public static BigDecimal addD(int scale, String... v) {
        //如果精确范围小于0，默认为2位小数
        if(scale < 0){
            scale = 2;
        }
        BigDecimal result = new BigDecimal("0");
        if (v != null && v.length > 0) {
            for (int i = 0; i <  v.length; i++) {
                result = result.add(new BigDecimal(org.apache.commons.lang3.StringUtils.isBlank(v[i]) ? "0" : v[i]));
            }
        }
        return roundD(result.doubleValue(), scale);
    }

    /**
     * 加法
     * @param scale 保留小数位数
     * @param v 需要的加数
     * @return double 两个参数的和
     */
    public static double add(int scale, BigDecimal... v) {
        //如果精确范围小于0，默认为2位小数
        if(scale < 0){
            scale = 2;
        }
        BigDecimal result = new BigDecimal("0");
        if (v != null && v.length > 0) {
            for (int i = 0; i <  v.length; i++) {
                result = result.add(v[i]);
            }
        }
        return round(result.doubleValue(), scale);
    }

    /**
     * 加法
     * @param scale 保留小数位数
     * @param v 需要的加数
     * @return BigDecimal 两个参数的和
     */
    public static BigDecimal addD(int scale, BigDecimal... v) {
        //如果精确范围小于0，默认为2位小数
        if(scale < 0){
            scale = 2;
        }
        BigDecimal result = new BigDecimal("0");
        if (v != null && v.length > 0) {
            for (int i = 0; i <  v.length; i++) {
                result = result.add(v[i]);
            }
        }
        return roundD(result.doubleValue(), scale);
    }

    /**
     * 加法
     * @param scale 保留小数位数
     * @param v 需要的加数
     * @return double 两个参数的和
     */
    public static double add(int scale, double... v) {
        //如果精确范围小于0，默认为2位小数
        if(scale < 0){
            scale = 2;
        }
        BigDecimal result = new BigDecimal("0");
        if (v != null && v.length > 0) {
            for (int i = 0; i <  v.length; i++) {
                result = result.add(new BigDecimal(v[i]));
            }
        }
        return round(result.doubleValue(), scale);
    }

    /**
     * 加法
     * @param scale 保留小数位数
     * @param v 需要的加数
     * @return BigDecimal 两个参数的和
     */
    public static BigDecimal addD(int scale, double... v) {
        //如果精确范围小于0，默认为2位小数
        if(scale < 0){
            scale = 2;
        }
        BigDecimal result = new BigDecimal("0");
        if (v != null && v.length > 0) {
            for (int i = 0; i <  v.length; i++) {
                result = result.add(new BigDecimal(v[i]));
            }
        }
        return roundD(result.doubleValue(), scale);
    }

    /**
     * 减法，不进行四舍五入
     * @param v1 被减数
     * @param v2 减数
     * @return 两个参数的差
     */
    public static double sub(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.subtract(b2).doubleValue();
    }

    /**
     * 减法，四舍五入
     * @param v1 被减数
     * @param v2 减数
     * @param scale 保留小数位数
     * @return 两个参数的差
     */
    public static double sub(double v1, double v2, int scale) {
        //如果精确范围小于0，默认为2位小数
        if(scale < 0){
            scale = 2;
        }
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return round(b1.subtract(b2).doubleValue(), scale);
    }

    /**
     * 减法，不进行四舍五入
     * @param v1 被减数
     * @param v2 减数
     * @return 两个参数的差
     */
    public static BigDecimal subD(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.subtract(b2);
    }

    /**
     * 减法，四舍五入
     * @param v1 被减数
     * @param v2 减数
     * @param scale 保留小数位数
     * @return 两个参数的差
     */
    public static BigDecimal subD(double v1, double v2, int scale) {
        //如果精确范围小于0，默认为2位小数
        if(scale < 0){
            scale = 2;
        }
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return roundD(b1.subtract(b2).doubleValue(), scale);
    }

    /**
     * 减法，不进行四舍五入
     * @param v1 被减数
     * @param v2 减数
     * @return 两个参数的差
     */
    public static double sub(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(org.apache.commons.lang3.StringUtils.isBlank(v1) ? "0" : v1);
        BigDecimal b2 = new BigDecimal(StringUtils.isBlank(v2) ? "0" : v2);
        return b1.subtract(b2).doubleValue();
    }

    /**
     * 减法，四舍五入
     * @param v1 被减数
     * @param v2 减数
     * @param scale 保留小数位数
     * @return 两个参数的差
     */
    public static double sub(String v1, String v2, int scale) {
        //如果精确范围小于0，默认为2位小数
        if(scale < 0){
            scale = 2;
        }
        BigDecimal b1 = new BigDecimal(org.apache.commons.lang3.StringUtils.isBlank(v1) ? "0" : v1);
        BigDecimal b2 = new BigDecimal(StringUtils.isBlank(v2) ? "0" : v2);
        return round(b1.subtract(b2).doubleValue(), scale);
    }

    /**
     * 减法，不进行四舍五入
     * @param v1 被减数
     * @param v2 减数
     * @return 两个参数的差
     */
    public static BigDecimal subD(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(org.apache.commons.lang3.StringUtils.isBlank(v1) ? "0" : v1);
        BigDecimal b2 = new BigDecimal(StringUtils.isBlank(v2) ? "0" : v2);
        return b1.subtract(b2);
    }

    /**
     * 减法，四舍五入
     * @param v1 被减数
     * @param v2 减数
     * @param scale 保留小数位数
     * @return 两个参数的差
     */
    public static BigDecimal subD(String v1, String v2, int scale) {
        //如果精确范围小于0，默认为2位小数
        if(scale < 0){
            scale = 2;
        }
        BigDecimal b1 = new BigDecimal(org.apache.commons.lang3.StringUtils.isBlank(v1) ? "0" : v1);
        BigDecimal b2 = new BigDecimal(StringUtils.isBlank(v2) ? "0" : v2);
        return roundD(b1.subtract(b2).doubleValue(), scale);
    }

    /**
     * 减法，不进行四舍五入
     * @param v1 被减数
     * @param v2 减数
     * @return 两个参数的差
     */
    public static double sub(BigDecimal v1, BigDecimal v2) {
        return v1.subtract(v2).doubleValue();
    }

    /**
     * 减法，四舍五入
     * @param v1 被减数
     * @param v2 减数
     * @param scale 保留小数位数
     * @return 两个参数的差
     */
    public static double sub(BigDecimal v1, BigDecimal v2, int scale) {
        //如果精确范围小于0，默认为2位小数
        if(scale < 0){
            scale = 2;
        }
        return round(v1.subtract(v2).doubleValue(), scale);
    }

    /**
     * 减法，不进行四舍五入
     * @param v1 被减数
     * @param v2 减数
     * @return 两个参数的差
     */
    public static BigDecimal subD(BigDecimal v1, BigDecimal v2) {
        return v1.subtract(v2);
    }

    /**
     * 减法，四舍五入
     * @param v1 被减数
     * @param v2 减数
     * @param scale 保留小数位数
     * @return 两个参数的差
     */
    public static BigDecimal subD(BigDecimal v1, BigDecimal v2, int scale) {
        //如果精确范围小于0，默认为2位小数
        if(scale < 0){
            scale = 2;
        }
        return roundD(v1.subtract(v2).doubleValue(), scale);
    }

    /**
     * 乘法，不进行四舍五入
     * @param v1 因数
     * @param v2 因数
     * @return 积
     */
    public static double mul(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.multiply(b2).doubleValue();
    }

    /**
     * 乘法，四舍五入
     * @param v1 因数
     * @param v2 因数
     * @param scale 保留小数位数
     * @return 积
     */
    public static double mul(double v1, double v2, int scale) {
        //如果精确范围小于0，默认为2位小数
        if(scale < 0){
            scale = 2;
        }
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return round(b1.multiply(b2).doubleValue(), scale);
    }

    /**
     * 乘法，四舍五入
     * @param v1 因数
     * @param v2 因数
     * @return 积
     */
    public static double mul(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.multiply(b2).doubleValue();
    }

    /**
     * 乘法，四舍五入，可设置保留小数
     * @param v1 因数
     * @param v2 因数
     * @param scale 保留小数位数
     * @return 积
     */
    public static double mul(String v1, String v2, int scale) {
        //如果精确范围小于0，默认为2位小数
        if(scale < 0){
            scale = 2;
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return round(b1.multiply(b2).doubleValue(), scale);
    }

    /**
     * 乘法，四舍五入
     * @param v1 因数
     * @param v2 因数
     * @return 积
     */
    public static double mul(BigDecimal v1, BigDecimal v2) {
        return v1.multiply(v2).doubleValue();
    }

    /**
     * 乘法，四舍五入，可设置保留小数
     * @param v1 因数
     * @param v2 因数
     * @param scale 保留小数位数
     * @return 积
     */
    public static double mul(BigDecimal v1, BigDecimal v2, int scale) {
        //如果精确范围小于0，默认为2位小数
        if(scale < 0){
            scale = 2;
        }
        return round(v1.multiply(v2).doubleValue(), scale);
    }

    /**
     * 乘法，不进行四舍五入
     * @param v1 因数
     * @param v2 因数
     * @return 积
     */
    public static BigDecimal mulD(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.multiply(b2);
    }

    /**
     * 乘法，四舍五入
     * @param v1 因数
     * @param v2 因数
     * @param scale 保留小数位数
     * @return 积
     */
    public static BigDecimal mulD(double v1, double v2, int scale) {
        //如果精确范围小于0，默认为2位小数
        if(scale < 0){
            scale = 2;
        }
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return roundD(b1.multiply(b2).doubleValue(), scale);
    }

    /**
     * 乘法
     * @param v1 因数
     * @param v2 因数
     * @return 积
     */
    public static BigDecimal mulD(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.multiply(b2);
    }

    /**
     * 乘法，四舍五入，可设置保留小数
     * @param v1 因数
     * @param v2 因数
     * @param scale 保留小数位数
     * @return 积
     */
    public static BigDecimal mulD(String v1, String v2, int scale) {
        //如果精确范围小于0，默认为2位小数
        if(scale < 0){
            scale = 2;
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return roundD(b1.multiply(b2).doubleValue(), scale);
    }

    /**
     * 乘法
     * @param v1 因数
     * @param v2 因数
     * @return 积
     */
    public static BigDecimal mulD(BigDecimal v1, BigDecimal v2) {
        return v1.multiply(v2);
    }

    /**
     * 乘法，四舍五入，可设置保留小数
     * @param v1 因数
     * @param v2 因数
     * @param scale 保留小数位数
     * @return 积
     */
    public static BigDecimal mulD(BigDecimal v1, BigDecimal v2, int scale) {
        //如果精确范围小于0，默认为2位小数
        if(scale < 0){
            scale = 2;
        }
        return roundD(v1.multiply(v2).doubleValue(), scale);
    }

    /**
     * 除法，四舍五入，可设置保留小数，如果除数为0则直接返回0
     * @param v1 被除数
     * @param v2 除数
     * @param scale 保留小数位数
     * @return 商
     */
    public static double div(double v1, double v2, int scale) {
        //如果精确范围小于0，默认为2位小数
        if(scale < 0){
            scale = 2;
        }
        if (v2 != 0D) {
            BigDecimal b1 = new BigDecimal(Double.toString(v1));
            BigDecimal b2 = new BigDecimal(Double.toString(v2));
            //保留两位小数，四舍五入
            return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
        } else {
            return new BigDecimal("0.00").doubleValue();
        }
    }

    /**
     * 除法，四舍五入，可设置保留小数，如果除数为0则直接返回0
     * @param v1 被除数
     * @param v2 除数
     * @param scale 保留小数位数
     * @return 商
     */
    public static double div(String v1, String v2, int scale) {
        //如果精确范围小于0，默认为2位小数
        if(scale < 0){
            scale = 2;
        }
        if (StringUtils.isNotBlank(v2)) {
            BigDecimal b1 = new BigDecimal(v1);
            BigDecimal b2 = new BigDecimal(v2);
            //保留两位小数，四舍五入
            return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
        } else {
            return new BigDecimal("0.00").doubleValue();
        }
    }

    /**
     * 除法，四舍五入，可设置保留小数，如果除数为0则直接返回0
     * @param v1 被除数
     * @param v2 除数
     * @param scale 保留小数位数
     * @return 商
     */
    public static BigDecimal divD(double v1, double v2, int scale) {
        //如果精确范围小于0，默认为2位小数
        if(scale < 0){
            scale = 2;
        }
        if (v2 != 0D) {
            BigDecimal b1 = new BigDecimal(Double.toString(v1));
            BigDecimal b2 = new BigDecimal(Double.toString(v2));
            //保留两位小数，四舍五入
            return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP);
        } else {
            return new BigDecimal("0.00");
        }
    }

    /**
     * 除法，四舍五入，可设置保留小数，如果除数为0则直接返回0
     * @param v1 被除数
     * @param v2 除数
     * @param scale 保留小数位数
     * @return 商
     */
    public static BigDecimal divD(String v1, String v2, int scale) {
        //如果精确范围小于0，默认为2位小数
        if(scale < 0){
            scale = 2;
        }
        if (StringUtils.isNotBlank(v2)) {
            BigDecimal b1 = new BigDecimal(v1);
            BigDecimal b2 = new BigDecimal(v2);
            //保留两位小数，四舍五入
            return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP);
        } else {
            return new BigDecimal("0.00");
        }
    }
}
